package charpter14.lynx;

import static net.mindview.util.Print.print;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

import polymorphism.music.Note;

interface Instrument {
	// Compile-time constant:
	int VALUE = 5; // static & final

	// Cannot have method definitions:
	void play(Note n); // Automatically public

	void adjust();
}

class Wind implements Instrument {
	public void play(Note n) {
		print(this + ".play() " + n);
	}

	public String toString() {
		return "Wind";
	}

	public void adjust() {
		print(this + ".adjust()");
	}
}

class Percussion implements Instrument {
	public void play(Note n) {
		print(this + ".play() " + n);
	}

	public String toString() {
		return "Percussion";
	}

	public void adjust() {
		print(this + ".adjust()");
	}
}

class Stringed implements Instrument {
	public void play(Note n) {
		print(this + ".play() " + n);
	}

	public String toString() {
		return "Stringed";
	}

	public void adjust() {
		print(this + ".adjust()");
	}
}

class Brass extends Wind {
	public String toString() {
		return "Brass";
	}
}

class Woodwind extends Wind {
	public String toString() {
		return "Woodwind";
	}
}

public class Twentysix {
	public static class InstrumentsProxyHandler implements InvocationHandler {

		public InstrumentsProxyHandler( Instrument instrument) {
			proxied = instrument;
		}
		
		Object proxied;
		
		private static void clearSpitValve(Wind instrument) {
			System.out.println("clearSpitValve "
					+ instrument.getClass().getSimpleName());
		}

		@Override
		public Object invoke(Object proxy, Method method, Object[] args)
				throws Throwable {
			if (proxied instanceof Wind) {
				clearSpitValve((Wind) proxied);
			}
			return method.invoke(proxied, args);
		}

	}

	static void tune(Instrument i) {
		// ...
		
		i.play(Note.MIDDLE_C);
	}

	static void tuneAll(Instrument[] e) {
		for (Instrument i : e)
			tune(newInstrument(i));
	}

	static public Instrument newInstrument(Instrument instrument){
		return (Instrument)Proxy.newProxyInstance(Instrument.class.getClassLoader(), new Class[]{Instrument.class}, new InstrumentsProxyHandler(instrument)); 
	}
	
	public static void main(String[] args) {
		// Upcasting during addition to the array:
		Instrument[] orchestra = { new Wind(), new Percussion(),
				new Stringed(), new Brass(), new Woodwind()};
		tuneAll(orchestra);
	}
}
